#pragma once

#include <iostream>
#include <functional>
#include <unordered_map>
#include <cassert>
#include "Sock.hpp"
#include "Log.hpp"
#include "Err.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

namespace tcpserver 
{
    class TcpServer;
    class Connection;
    static const uint16_t defaultport = 8080;
    static const int num = 64;

    using func_t = std::function<void(Connection *)>;
    // using hander_t = std::function<void(const std::string &package)>;

    // class Revent
    // {
    // public:
    //     int sock;
    //     uint32_t revents;
    // }

    class Connection // 让每个套接字有自己独立的缓冲区，把每一个套接字看作成一个Connection
    {
    public:
        Connection(int sock, TcpServer *tsp) : _sock(sock), _tsp(tsp)
        {
        }
        void Register(func_t r, func_t s, func_t e)
        {
            _recver = r;
            _sender = s;
            _excpter = e;
        }
        void Close()
        {
            close(_sock);
        }
        ~Connection()
        {
        }

    public:
        int _sock;
        std::string _inbuffer;  // 输入缓冲区
        std::string _outbuffer; // 输出缓冲区

        func_t _recver;  // 向_sock读
        func_t _sender;  // 向_sock写
        func_t _excpter; // 处理_sock IO的时候上面的异常事件

        // 可以被省略，我想加一下,拿来使用TcpServer的类内方法
        TcpServer *_tsp; // t:tcp  s:server  p:指针

        uint64_t lastatime;// 拿来记录每个conn，最近的访问时间
    };

    class TcpServer // Reaction
    {
    private:
        void Recver(Connection *conn)
        {
            // logMessage(DEBUG, "Recver in");
            char buffer[1024];
            conn->lastatime = time(nullptr);
            while (true)
            {
                ssize_t s = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
                int saveErron = errno;
                if (s > 0)
                {
                    buffer[s] = 0;
                    std::cout << buffer << std::endl;
                    conn->_inbuffer += buffer; // 将读到的数据入队列
                }
                else if (s == 0)
                {
                    std::cout << "client quit" << std::endl;
                    if (conn->_excpter)
                    {
                        conn->_excpter(conn);
                        return;
                    }
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break; // 底层把数据读完了
                    else if (errno == EINTR)
                        continue; // 信号中断
                    else
                    {
                        if (conn->_excpter)
                        {
                            conn->_excpter(conn);
                            return;
                        }
                    }
                }
            }
            _server(conn);
            // logMessage(DEBUG, "%d -> %s", conn->_sock, conn->_inbuffer.c_str());
            // logMessage(DEBUG, "Recver out");
        }
        void Sender(Connection *conn)
        {
            ssize_t s = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
            conn->lastatime = time(nullptr);

            while (true)
            {
                if (s > 0)
                {
                    if (conn->_outbuffer.empty())
                    {
                        // EnableReadWrite(conn, true, false);
                        break;
                    }
                    else
                        conn->_outbuffer.erase(0, s);
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn->_excpter)
                        {
                            conn->_excpter(conn);
                            break;
                        }
                    }
                }
            }

            // 如果没有发送完毕，需要对对于的sock开启事件的关心，如果发完了，我们要关闭对写事件的关系！
            if (!conn->_outbuffer.empty())
                conn->_tsp->EnableReadWrite(conn, true, true);
            else
                conn->_tsp->EnableReadWrite(conn, true, false);
        }
        void Excpter(Connection *conn)
        {
            logMessage(DEBUG,"Excpter begin");
            _epoller.Control(conn->_sock,0,EPOLL_CTL_DEL);
            conn->Close();
            _connection.erase(conn->_sock);

            logMessage(DEBUG,"关闭%d 文件描述符的所有资源",conn->_sock);
            delete conn;

        }
        void Accepter(Connection *conn)
        {
            for (;;)
            {
                std::string clientip;
                uint16_t clientport;
                int err = 0;
                int sock = _sock.Accept(&clientip, &clientport, &err);
                if (sock >= 0)
                {
                    AddConnection(sock, EPOLLIN | EPOLLET,
                                  std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Excpter, this, std::placeholders::_1));
                    logMessage(DEBUG, "get a new link,info: [%s:%d]", clientip.c_str(), clientport);
                }
                else
                {
                    if (err == EAGAIN || err == EWOULDBLOCK)
                        break; // 底层把数据读完了
                    else if (err == EINTR)
                        continue; // 信号中断
                    else
                        break; // 出错了
                }
            }
        }
        void AddConnection(int sock, uint32_t events, func_t recver, func_t sender, func_t excpter)
        {
            // 1.首先要为sock创建Connection，并初始化，并添加到_connection
            if (events & EPOLLET)
                Util::SetNonBlock(sock);
            Connection *conn = new Connection(sock, this); // ?
            // 2.给对应的sock设置对应的回调处理方法
            conn->Register(recver, sender, excpter);

            // 2.其次将sock与它要关心的事件"写透式"注册到epoll中，让epoll帮我们关心
            bool r = _epoller.AddEvent(sock, events);
            assert(r);
            (void)r;

            // 3.将kv添加到_connection
            _connection.insert(std::pair<int, Connection *>(sock, conn));

            logMessage(DEBUG, "add new sock : %d in epoll and unordered_map", sock);
        }
        bool IsConnectionExists(int sock)
        {
            auto iter = _connection.find(sock);
            return iter != _connection.end();
        }
        void Loop(int timeout)
        {
            int n = _epoller.Wait(_revs, _num, timeout); // 获取已经就绪的事件
            for (int i = 0; i < n; i++)
            {
                int sock = _revs[i].data.fd;
                uint32_t events = _revs[i].events;

                // 将所有的异常问题，全部转换 成为读写问题
                if (events & EPOLLERR)
                    events |= (EPOLLIN | EPOLLOUT); // : 表示对应的文件描述符发生错误
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT); // : 表示对应的文件描述符被挂断  对方关闭文件描述符，epoll会先知道，而不是recv/read
                // listen事件就绪
                // isExists(sock)判断sock是否真的在哈希表里面，这样安全一点
                // if(_connection[sock]->_recver):防止有时候忘记设置回调方法
                if (IsConnectionExists(sock) && (events & EPOLLIN) && _connection[sock]->_recver) // 不论是监听事件，还是普通事件都是这样调用回调方法
                    _connection[sock]->_recver(_connection[sock]);
                if ((events & EPOLLOUT) && (events & EPOLLIN) && _connection[sock]->_sender)
                    _connection[sock]->_sender(_connection[sock]);
                // ?
            }
        }

    public:
        TcpServer(func_t func, uint16_t port = defaultport) : _server(func), _port(port), _revs(nullptr)
        {
        }
        void initServer()
        {
            // 1.创建socket
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();
            // 2.构建Epoll
            _epoller.Create();
            // 3.将目前唯一的一个sock，添加到epoller中,之前需要先将对应的fd设置成为非阻塞
            // _listensock也是一个socket啊，也要看作成为一个Connection，虽然不需要缓冲区等等
            AddConnection(_sock.Fd(), EPOLLIN | EPOLLET, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);

            _revs = new struct epoll_event[num];
            _num = num;
        }

        void EnableReadWrite(Connection *conn, bool readable, bool writeable) // 通过一个套接字，打开写事件，或者读事件的关心
        {
            uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0);
            _epoller.Control(conn->_sock, event, EPOLL_CTL_MOD);
        }

        // 事件派发器
        void Dispatch()
        {
            int timeout = 1000;
            while (true)
            {
                Loop(timeout);
                // logMessage(DEBUG, "timeout ....");

                // 遍历_connection,计算每一个链接已经多长时间没有动了,每次读写我就记录一次
                // 超过5分钟我可以把这个链接删除
            }
        }
        ~TcpServer()
        {
            _sock.Close();
            _epoller.Close();
            if (_revs == nullptr)
                delete[] _revs;
        }

    private:
        Sock _sock; // 这个结构体里面就包含_listensock
        uint16_t _port;
        Epoller _epoller;
        std::unordered_map<int, Connection *> _connection;
        struct epoll_event *_revs;
        int _num;
        // hander_t _hander; 让我的tcpserver只负责读写
        func_t _server; // 这个负责业务逻辑
    };

}